package com.platform.comm.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;

/**
 * @author liujizheng
 * @version 1.0
 * @ClassName ClassScanner
 * @Description 类扫描器
 * @date 2021/7/8 15:51
 */
public class ClassScanner {

    private static Logger logger = LoggerFactory.getLogger(ClassScanner.class);
    private static final String PATH_SEPARATOR = "/";

    /**
     * 扫描包下类，并根据注解进行筛选
     *
     * @param basePackage          扫描包
     * @param annotationType       过滤的注解类型
     * @return 扫描到的类
     * @throws IOException 异常
     */
    public static List<Class<?>> scan(String basePackage, Class<? extends Annotation> annotationType) throws IOException {
        assert annotationType != null;
        List<Class<?>> classList = new ArrayList<>();
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        // 将包名转换成路径模式 如：com.platform => com/xzkingdee/**/*.class
        String scanPackage = basePackage.contains(PATH_SEPARATOR) ? basePackage :
                ClassUtils.convertClassNameToResourcePath(basePackage) + "/**/*" + ClassUtils.CLASS_FILE_SUFFIX;
        scanPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + scanPackage;
        logger.info("扫描包名：" + scanPackage);
        Resource[] resources = resolver.getResources(scanPackage);
        logger.info("扫描到的类数量：" + resources.length);
        for(Resource resource: resources) {
            String file = resource.getURL().getFile();
            String path;
            if (file.contains("!")) {
                // jar 包中类路径处理
                String[] splits = file.split("!/");
                path = splits[splits.length-1];
            } else {
                // directory 中类路径处理
                String[] splits = file.split("classes/");
                path = splits[splits.length-1];
            }

            // 将路径转换成包类格式
            String className = ClassUtils.convertResourcePathToClassName(path).replace(ClassUtils.CLASS_FILE_SUFFIX, "");
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(annotationType)) {
                    classList.add(clazz);
                }
            } catch (Error | ClassNotFoundException e) {
                logger.warn("class[{}] not found!, ex: {}", className, e.getMessage());
            }
        }
        return classList;
    }
}
